<?php
// $Id: activerecord.php 2553 2009-06-10 21:29:21Z dualface $


/**
 * 定义 QDB_ActiveRecord_Abstract 类
 *
 * @link http://qeephp.com/
 * @copyright Copyright (c) 2006-2009 Qeeyuan Inc. {@link http://www.qeeyuan.com}
 * @license New BSD License {@link http://qeephp.com/license/}
 * @version $Id: activerecord.php 2553 2009-06-10 21:29:21Z dualface $
 * @package orm
 */

/**
 * QDB_ActiveRecord_Abstract 类实现了 Active Record 模式
 *
 * @author YuLei Liao <liaoyulei@qeeyuan.com>
 * @version $Id: activerecord.php 2553 2009-06-10 21:29:21Z dualface $
 * @package orm
 */
abstract class QDB_ActiveRecord_Abstract implements QDB_ActiveRecord_Callbacks, QDB_ActiveRecord_Interface, ArrayAccess {
	/**
	 * 对象所有属性的值
	 *
	 * @var array
	 */
	protected $_props;
	
	/**
	 * 当前 ActiveRecord 对象的类名称
	 *
	 * @var string
	 */
	protected $_class_name;
	
	/**
	 * 对象的 ID
	 *
	 * 如果对象的 ID 是由多个属性组成，则 $_id 是一个由多个属性值组成的名值对。
	 *
	 * @var mixed
	 */
	protected $_id = false;
	
	/**
	 * ActiveRecord 继承类使用的 Meta 对象
	 *
	 * @var QDB_ActiveRecord_Meta
	 */
	private static $_meta;
	
	/**
	 * 指示对象的哪些属性已经做了修改
	 */
	private $_changed_props = array ();
	
	/**
	 * 异常捕捉器
	 *
	 * @var array
	 * @access private
	 */
	public $__exception_trap = array ();
	
	/**
	 * 构造函数
	 *
	 * @param array|object $data 包含数据的名值对
	 * @param int $names_style 名值对的键名风格
	 * @param boolean $from_storage 是否从存储器载入数据
	 */
	function __construct($data = null, $names_style = QDB::PROP, $from_storage = false) {
		
		$this->_class_name = get_class ( $this );
		
		if (! isset ( self::$_meta [$this->_class_name] )) {
			self::$_meta [$this->_class_name] = QDB_ActiveRecord_Meta::instance ( $this->_class_name );
		}
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		// 设置对象属性默认值
		$this->_props = $meta->default_props;
		
		if (is_array ( $data ) || $data instanceof Iterator) {
			$this->changeProps ( $data, $names_style, null, $from_storage, true );
		}
		// 触发 after_initialize 事件
		$this->_after_initialize ();
		$this->_event ( self::AFTER_INITIALIZE );
		$this->_after_initialize_post ();
	}
	
	/**
	 * 获得对象的 ID（既对象在数据库中的主键值）
	 *
	 * 如果对象的 ID 是由多个属性组成，则 id() 方法会返回一个数组。
	 *
	 * @param boolean $cached 默认返回缓存值
	 * FIXED!
	 * @return mixed
	 */
	function id($cached = true) {
		if ($cached && $this->_id !== false) {
			return $this->_id;
		}
		
		$id = array ();
		foreach ( self::$_meta [$this->_class_name]->idname as $name ) {
			$id [$name] = $this->{$name};
		}
		
		if (count ( $id ) == 1) {
			$id = reset ( $id );
		}
		
		$this->_id = $id;
		return $id;
	}
	
	/**
	 * 获得对象的 ID 属性名（对象在数据库中的主键字段名）
	 *
	 * 如果对象的 ID 是由多个属性组成，则 idname() 方法会返回一个包含多个属性名的数组。
	 *
	 * @return string|array
	 */
	function idname() {
		if (self::$_meta [$this->_class_name]->idname_count > 1) {
			return self::$_meta [$this->_class_name]->idname;
		} else {
			return reset ( self::$_meta [$this->_class_name]->idname );
		}
	}
	
	/**
	 * 返回当前对象的元信息对象
	 *
	 * @return QDB_ActiveRecord_Meta
	 */
	function getMeta() {
		return self::$_meta [$this->_class_name];
	}
	
	/**
	 * 保存对象到数据库
	 *
	 * @param int $recursion 保存操作递归到多少层
	 * @param string $save_method 保存对象的方法
	 *
	 * @return QDB_ActiveRecord_Abstract 连贯接口
	 */
	function save($recursion = 99, $save_method = 'save') {
		$inherit_type_field = self::$_meta [$this->_class_name]->inherit_type_field;
		if ($inherit_type_field && empty ( $this->_props [$inherit_type_field] )) {
			$this->_props [$inherit_type_field] = $this->_class_name;
		}
		
		$this->_before_save ();
		$this->_event ( self::BEFORE_SAVE );
		$this->_before_save_post ();
		
		$id = $this->id ( false ); // FIXED! 不使用缓存
		

		try {
			switch (strtolower ( $save_method )) {
				case 'create' :
					$exception_event = self::CREATE_EXCEPTION;
					$this->_create ( $recursion );
					break;
				case 'update' :
					$exception_event = self::UPDATE_EXCEPTION;
					$this->_update ( $recursion );
					break;
				case 'replace' :
					$exception_event = self::REPLACE_EXCEPTION;
					$this->_replace ( $recursion );
					break;
				case 'save' :
				default :
					if (! is_array ( $id )) {
						// 单一主键的情况下，如果 $id 为空，则 create，否则 update
						if (empty ( $id )) {
							$exception_event = self::CREATE_EXCEPTION;
							$this->_create ( $recursion );
						} else {
							$exception_event = self::UPDATE_EXCEPTION;
							$this->_update ( $recursion );
						}
					} else {
						// 复合主键的情况下，则使用 replace 方式
						$exception_event = self::REPLACE_EXCEPTION;
						$this->_replace ( $recursion );
					}
			}
			
			$this->_after_save ();
			$this->_event ( self::AFTER_SAVE );
			$this->_after_save_post ();
		} catch ( Exception $ex ) {
			if (! empty ( $this->__exception_trap [$exception_event] )) {
				foreach ( $this->__exception_trap [$exception_event] as $callback ) {
					array_unshift ( $callback [1], $this );
					array_push ( $callback [1], $ex );
					call_user_func_array ( $callback [0], $callback [1] );
				}
			}
			
			throw $ex;
		}
		
		$this->_id = false; // FIXED! 清除缓存
		return $this;
	}
	
	/**
	 * 返回当前对象的一个复制品
	 *
	 * 返回的复制品没有 ID 值，因此在保存时将会创建一个新记录。
	 * __clone() 操作仅限当前对象的属性，对于关联的对象不会进行克隆。
	 *
	 * @return QDB_ActiveRecord_Abstract 复制的 ActiveRecord 对象
	 */
	function __clone() {
		foreach ( self::$_meta [$this->_class_name]->idname as $name ) {
			$this->_props [$name] = self::$_meta [$this->_class_name]->table_meta [$name] ['default'];
		}
		
		$this->_id = false; // FIXED! 清除缓存
	}
	
	/**
	 * 判断对象是否有特定的属性
	 *
	 * @return boolean
	 */
	function hasProp($prop_name) {
		return isset ( self::$_meta [$this->_class_name]->props [$prop_name] );
	}
	
	/**
	 * 从数据库重新读取当前对象的属性，不影响关联的对象
	 */
	function reload() {
		if (self::$_meta [$this->_class_name]->idname_count > 1) {
			// 复合主键
			$where = $this->id ();
		} else {
			// 单一主键
			$where = array (reset ( self::$_meta [$this->_class_name]->idname ) => $this->id () );
		}
		
		$row = self::$_meta [$this->_class_name]->find ( $where )->asArray ()->recursion ( 0 )->query ();
		$this->changeProps ( $row, QDB::FIELD, null, true );
	}
	
	/**
	 * 验证对象属性，失败抛出异常
	 *
	 * @param string $mode 验证模式，可以是 general、create、update
	 *
	 * @return array|null 为验证通过的项目及错误信息
	 */
	function validate($mode = 'general') {
		$this->_before_validate ();
		$this->_event ( self::BEFORE_VALIDATE );
		$this->_before_validate_post ();
		
		// 根据不同的验证模式，引发不同的事件，并且确定要验证的属性
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		if ($mode == 'create') {
			$this->_before_validate_on_create ();
			$this->_event ( self::BEFORE_VALIDATE_ON_CREATE );
			$this->_before_validate_on_create_post ();
			
			$validation_props = array_keys ( $meta->props2fields );
			foreach ( $meta->idname as $idname ) {
				unset ( $validation_props [$idname] );
			}
		} elseif ($mode == 'update') {
			$this->_before_validate_on_update ();
			$this->_event ( self::BEFORE_VALIDATE_ON_UPDATE );
			$this->_before_validate_on_update_post ();
			$validation_props = $this->changes ();
		}
		
		$error = $meta->validate ( $this->_props, $validation_props, $mode );
		
		if (! empty ( $error )) {
			throw new QDB_ActiveRecord_ValidateFailedException ( $error, $this );
		}
		
		if ($mode == 'create') {
			$this->_after_validate_on_create ();
			$this->_event ( self::AFTER_VALIDATE_ON_CREATE );
			$this->_after_validate_on_create_post ();
		} elseif ($mode == 'update') {
			$this->_after_validate_on_update ();
			$this->_event ( self::AFTER_VALIDATE_ON_UPDATE );
			$this->_after_validate_on_update_post ();
		}
		
		$this->_after_validate ();
		$this->_event ( self::AFTER_VALIDATE );
		$this->_after_validate_post ();
	}
	
	/**
	 * 销毁对象对应的数据库记录
	 */
	function destroy() {
		$id = $this->id ( false ); // FIXED! 不使用缓存
		if (empty ( $id )) {
			throw new QDB_ActiveRecord_DestroyWithoutIdException ( $this );
		}
		
		// 引发 before_destroy 事件
		$this->_before_destroy ();
		$this->_event ( self::BEFORE_DESTROY );
		$this->_before_destroy_post ();
		
		// 处理关联的对象
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		foreach ( $meta->associations as $assoc ) {
			$assoc->onSourceDestroy ( $this );
		}
		
		// 确定删除当前对象的条件
		if ($meta->idname_count > 1) {
			$where = $id;
		} else {
			$where = array (reset ( $meta->idname ) => $id );
		}
		
		// 从数据库中删除当前对象
		$meta->table->delete ( $where );
		
		// 引发 after_destroy 事件
		$this->_after_destroy ();
		$this->_event ( self::AFTER_DESTROY );
		$this->_after_destroy_post ();
	
		//$this->_id = false; // FIXED! 清除缓存
	// destroy() 并不改变主键值，无需更改 $this->_id 属性
	}
	
	/**
	 * 批量设置对象的属性值
	 *
	 * 如果指定了 $attr_accessible 参数，则会忽略 ActiveRecord 类的 attr_accessible 和 attr_protected 设置。
	 *
	 * @param array|object $arr 名值对数组
	 * @param int $names_style 键名是属性名还是字段名
	 * @param array|string $attr_accessible 指定哪些属性允许设置
	 * @param boolean $_from_storage 内部参数
	 * @param boolean $_ignore_readonly 内部参数
	 *
	 * @return QDB_ActiveRecord_Abstract 连贯接口
	 */
	function changeProps($arr, $names_style = QDB::PROP, $attr_accessible = null, $_from_storage = false, $_ignore_readonly = false) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		if ($attr_accessible) {
			$attr_accessible = array_flip ( Q::normalize ( $attr_accessible ) );
			$check_attr_accessible = true;
		} else {
			$check_attr_accessible = ! empty ( $meta->attr_accessible );
			$attr_accessible = $meta->attr_accessible;
		}
		
		// 将数组赋值给对象属性
		foreach ( $arr as $prop_name => $value ) {
			if ($names_style == QDB::FIELD) {
				if (! isset ( $meta->fields2props [$prop_name] )) {
					continue;
				}
				$prop_name = $meta->fields2props [$prop_name];
			} elseif (! isset ( $meta->props [$prop_name] )) {
				continue;
			}
			
			if ($_from_storage) {
				if ($meta->props [$prop_name] ['virtual']) {
					$this->{$prop_name} = $value;
					unset ( $this->_changed_props [$prop_name] );
				} else {
					$this->_props [$prop_name] = is_null ( $value ) ? NULL : self::_typed ( $value, $meta->props [$prop_name] ['ptype'] );
				}
			} else {
				if ($check_attr_accessible) {
					if (! isset ( $attr_accessible [$prop_name] )) {
						continue;
					}
				} elseif (isset ( $meta->attr_protected [$prop_name] )) {
					continue;
				}
				
				if ($_ignore_readonly) {
					$this->changePropForce ( $prop_name, $value );
				} else {
					$this->{$prop_name} = $value;
				}
			
			}
		}
		
		return $this;
	}
	
	/**
	 * 强制改变一个属性的值，忽略属性的 readonly 设置
	 *
	 * @param string $prop_name 要改变的属性名
	 * @param mixed $prop_value 属性的值
	 *
	 * @return QDB_ActiveRecord_Abstract 连贯接口
	 */
	function changePropForce($prop_name, $prop_value) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		if (! isset ( $meta->props [$prop_name] )) {
			return $this;
		}
		
		try {
			$ro = $meta->props [$prop_name] ['readonly'];
			self::$_meta [$this->_class_name]->props [$prop_name] ['readonly'] = false;
			$this->{$prop_name} = $prop_value;
			self::$_meta [$this->_class_name]->props [$prop_name] ['readonly'] = $ro;
		} catch ( Exception $ex ) {
			self::$_meta [$this->_class_name]->props [$prop_name] ['readonly'] = $ro;
			throw $ex;
		}
		return $this;
	}
	
	/**
	 * 确认对象或指定的对象属性是否已经被修改
	 *
	 * @param string|array $props_name
	 *
	 * @return boolean
	 */
	function changed($props_name = null) {
		if (is_null ( $props_name )) {
			return ! empty ( $this->_changed_props );
		}
		
		$props_name = Q::normalize ( $props_name );
		foreach ( $props_name as $prop_name ) {
			if (isset ( $this->_changed_props [$prop_name] )) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 将指定的属性设置为“脏”状态
	 *
	 * @param string|array $props_name
	 *
	 * @return QDB_ActiveRecord_Abstract 连贯接口
	 */
	function willChanged($props_name) {
		$props_name = Q::normalize ( $props_name );
		foreach ( $props_name as $prop_name ) {
			if (! isset ( self::$_meta [$this->_class_name]->props [$prop_name] )) {
				continue;
			}
			$this->_changed_props [$prop_name] = $prop_name;
		}
		
		return $this;
	}
	
	/**
	 * 获得修改过的属性
	 *
	 * @return array
	 */
	function changes() {
		return $this->_changed_props;
	}
	
	/**
	 * 清除所有属性或指定的“脏”状态
	 *
	 * @param string|array $props
	 *
	 * @return QDB_ActiveRecord_Abstract 连贯接口
	 */
	function cleanChanges($props = null) {
		if ($props) {
			$props = Q::normalize ( $props );
			foreach ( $props as $prop ) {
				unset ( $this->_changed_props [$prop] );
			}
		} else {
			$this->_changed_props = array ();
		}
	}
	
	/**
	 * 获得包含对象所有属性的数组
	 *
	 * @param int $recursion
	 * @param int $names_style
	 *
	 * @return array
	 */
	function toArray($recursion = 99, $names_style = QDB::PROP) {
		$data = array ();
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		foreach ( $meta->props as $prop_name => $config ) {
			if ($names_style == QDB::PROP) {
				$name = $prop_name;
			} else {
				$name = $meta->props2fields [$prop_name];
			}
			
			if ($config ['assoc']) {
				if ($recursion > 0 && isset ( $this->_props [$prop_name] )) {
					$data [$name] = $this->{$prop_name}->toArray ( $recursion - 1, $names_style );
				}
			} elseif ($config ['virtual'] && empty ( $config ['getter'] )) {
				continue;
			} else {
				$data [$name] = $this->{$prop_name};
			}
		}
		return $data;
	}
	
	/**
	 * 返回对象所有属性的 JSON 字符串
	 *
	 * @param int $recursion
	 * @param int $names_style
	 *
	 * @return string
	 */
	function toJSON($recursion = 99, $names_style = QDB::PROP) {
		return json_encode ( $this->toArray ( $recursion, $names_style ) );
	}
	
	/**
	 * 魔法方法，实现对象属性值的读取
	 *
	 * @param string $prop_name
	 *
	 * @return mixed
	 */
	function __get($prop_name) {
		if (! isset ( self::$_meta [$this->_class_name]->props [$prop_name] )) {
			throw new QDB_ActiveRecord_UndefinedPropException ( $this->_class_name, $prop_name );
		}
		
		$config = self::$_meta [$this->_class_name]->props [$prop_name];
		if (! empty ( $config ['getter'] )) {
			// 如果指定了属性的 getter，则通过 getter 方法来获得属性值
			list ( $callback, $custom_parameters ) = $config ['getter'];
			if (! is_array ( $callback )) {
				$callback = array ($this, $callback );
				$args = array ($prop_name, $custom_parameters, & $this->_props );
			} else {
				$args = array ($this, $prop_name, $custom_parameters, & $this->_props );
			}
			return call_user_func_array ( $callback, $args );
		}
		
		if (! isset ( $this->_props [$prop_name] ) && $config ['assoc']) {
			// 如果属性是一个关联，并且没有值，则通过 QDB_ActiveRecord_Meta::relatedObjects() 获得关联对象
			$this->_props [$prop_name] = self::$_meta [$this->_class_name]->relatedObjects ( $this, $prop_name );
		}
		
		return $this->_props [$prop_name];
	}
	
	/**
	 * 魔法方法，实现对象属性的设置
	 *
	 * @param string $prop_name
	 * @param mixed $value
	 */
	function __set($prop_name, $value) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		if (! isset ( $meta->props [$prop_name] )) {
			throw new QDB_ActiveRecord_UndefinedPropException ( $this->_class_name, $prop_name );
		}
		
		$config = $meta->props [$prop_name];
		if ($config ['readonly']) {
			throw new QDB_ActiveRecord_ChangingReadonlyPropException ( $this->_class_name, $prop_name );
		}
		
		if (! empty ( $config ['setter'] )) {
			// 如果指定了属性的 setter，则通过 setter 方法来修改属性值
			list ( $callback, $custom_parameters ) = $config ['setter'];
			if (! is_array ( $callback )) {
				$callback = array ($this, $callback );
				$args = array ($value, $prop_name, $custom_parameters, & $this->_props );
			} else {
				$args = array ($this, $value, $prop_name, $custom_parameters, & $this->_props );
			}
			return call_user_func_array ( $callback, $args );
		}
		
		if ($config ['assoc']) {
			// 在指定关联对象时，要进行类型检查
			if ($config ['assoc'] == QDB::HAS_ONE || $config ['assoc'] == QDB::BELONGS_TO) {
				if ($value instanceof $config ['assoc_class']) {
					$this->_props [$prop_name] = $value;
				} else {
					throw new QDB_ActiveRecord_SettingPropTypeMismatchException ( $this->_class_name, $prop_name, $config ['assoc_class'], gettype ( $value ) );
				}
			} else {
				if (is_array ( $value )) {
					$this->_props [$prop_name] = QColl::createFromArray ( $value, $config ['assoc_class'] );
				} elseif ($value instanceof Iterator) {
					$this->_props [$prop_name] = $value;
				} else {
					throw new QDB_ActiveRecord_SettingPropTypeMismatchException ( $this->_class_name, $prop_name, 'Iterator', gettype ( $value ) );
				}
			}
			
			$this->_changed_props [$prop_name] = $prop_name;
		} elseif ($this->_props [$prop_name] !== $value) {
			$this->_props [$prop_name] = self::_typed ( $value, $config ['ptype'] );
			$this->_changed_props [$prop_name] = $prop_name;
		}
	}
	
	/**
	 * 魔法方法，实现对 isset() 的支持
	 *
	 * @param string $prop_name
	 *
	 * @return boolean
	 */
	function __isset($prop_name) {
		return array_key_exists ( $prop_name, $this->_props );
	}
	
	/**
	 * 魔法方法，用于调用行为插件为对象添加的方法
	 *
	 * @param string $method
	 * @param array $args
	 *
	 * @return mixed
	 */
	function __call($method, array $args) {
		if (isset ( self::$_meta [$this->_class_name]->methods [$method] )) {
			$callback = self::$_meta [$this->_class_name]->methods [$method];
			foreach ( $args as $arg ) {
				array_push ( $callback [1], $arg );
			}
			array_unshift ( $callback [1], $this );
			return call_user_func_array ( $callback [0], $callback [1] );
		}
		
		// getXX() 和 setXX() 方法
		$prefix = substr ( $method, 0, 3 );
		if ($prefix == 'get') {
			$prop_name = substr ( $method, 3 );
			return $this->{$prop_name};
		} elseif ($prefix == 'set') {
			$prop_name = substr ( $method, 3 );
			$this->{$prop_name} = reset ( $args );
			return null;
		}
		
		throw new QDB_ActiveRecord_CallToUndefinedMethodException ( $this->_class_name, $method );
	}
	
	/**
	 * ArrayAccess 接口方法
	 *
	 * @param string $prop_name
	 *
	 * @return boolean
	 */
	function offsetExists($prop_name) {
		return array_key_exists ( $prop_name, $this->_props );
	}
	
	/**
	 * ArrayAccess 接口方法
	 *
	 * @param string $prop_name
	 * @param mixed $value
	 */
	function offsetSet($prop_name, $value) {
		$this->{$prop_name} = $value;
	}
	
	/**
	 * ArrayAccess 接口方法
	 *
	 * @param string $prop_name
	 *
	 * @return boolean
	 */
	function offsetGet($prop_name) {
		return $this->{$prop_name};
	}
	
	/**
	 * ArrayAccess 接口方法
	 *
	 * @param string $prop_name
	 */
	function offsetUnset($prop_name) {
		$this->{$prop_name} = null;
	}
	
	/**
	 * 用于 QColl 的回调方法
	 */
	static function _qcoll_callback() {
		return array ('tojson' => 'multiToJSON' );
	}
	
	/**
	 * 将多个 ActiveRecord 对象转换为 JSON 字符串
	 *
	 * @param array $objects
	 * @param int $recursion
	 * @param int $names_style
	 *
	 * @return string
	 */
	static function multiToJSON(array $objects, $recursion = 99, $names_style = QDB::PROP) {
		$arr = array ();
		while ( list ( , $obj ) = each ( $objects ) ) {
			$arr [] = $obj->toArray ( $recursion, $names_style );
		}
		return json_encode ( $arr );
	}
	
	/**
	 * 调用 ActiveRecord 对象的动态方法
	 *
	 * @param string $method
	 *
	 * @return mixed
	 */
	protected function _method($method) {
		$args = func_get_args ();
		array_shift ( $args );
		return $this->__call ( $method, $args );
	}
	
	/**
	 * 触发事件
	 *
	 * @param int $event
	 */
	protected function _event($event) {
		$meta = self::$_meta [$this->_class_name];
		if (empty ( $meta->callbacks [$event] )) {
			return;
		}
		
		foreach ( $meta->callbacks [$event] as $callback ) {
			array_unshift ( $callback [1], $this );
			call_user_func_array ( $callback [0], $callback [1] );
		}
	}
	
	/**
	 * 在数据库中创建对象
	 *
	 * @param int $recursion
	 */
	protected function _create($recursion = 99) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		// 根据 create_autofill 设置对属性进行填充
		$this->_autofill ( 'create' );
		
		// 引发 before_create 事件
		$this->_before_create ();
		$this->_event ( self::BEFORE_CREATE );
		$this->_before_create_post ();
		
		// 进行 create 验证
		$this->validate ( 'create', true );
		
		// 特别处理 BELONGS_TO 关联
		foreach ( $meta->belongsto_props as $prop_name => $assoc ) {
			/* @var $assoc QDB_ActiveRecord_Association_BelongsTo */
			$mapping_name = $assoc->mapping_name;
			$source_key = $assoc->source_key;
			
			if (empty ( $this->_props [$mapping_name] )) {
				if (empty ( $this->_props [$source_key] )) {
					if ($this->_props [$source_key] === $meta->props [$source_key] ['default_value'] && ! is_null ( $meta->props [$source_key] ['default_value'] )) {
						$this->changePropForce ( $source_key, $meta->props [$source_key] ['default_value'] );
					} else {
						// 如果BELONGS TO关联设置了允许空值，则跳过抛出异常
						if (empty ( $assoc->source_meta->props [$mapping_name] ['assoc_params'] ['skip_empty'] )) {
							throw new QDB_ActiveRecord_ExpectsAssocPropException ( $this->_class_name, $mapping_name );
						}
					}
				}
			} else {
				$belongsto = $this->_props [$mapping_name];
				/* @var $belongsto QDB_ActiveRecord_Abstract */
				$this->changePropForce ( $source_key, $belongsto->{$assoc->target_key} );
			}
		}
		
		// 准备要保存到数据库的数据
		$save_data = array ();
		foreach ( $this->_props as $prop_name => $value ) {
			if (isset ( $meta->create_reject [$prop_name] ) || $meta->props [$prop_name] ['virtual']) {
				continue;
			}
			$save_data [$meta->props2fields [$prop_name]] = $value;
		}
		
		// 将名值对保存到数据库
		$pk = $meta->table->insert ( $save_data, true );
		
		// 将获得的主键值指定给对象
		foreach ( $pk as $field_name => $field_value ) {
			$this->_props [$meta->props2fields [$field_name]] = $field_value;
		}
		
		// 遍历关联的对象，并调用对象的save()方法
		foreach ( $meta->associations as $prop => $assoc ) {
			if ($assoc->type == QDB::BELONGS_TO || ! isset ( $this->_props [$prop] )) {
				continue;
			}
			
			/* @var $assoc QDB_ActiveRecord_Association_Abstract */
			
			$assoc->init ();
			$source_key_value = $this->{$assoc->source_key};
			
			if (strlen ( $source_key_value ) == 0) {
				throw new QDB_ActiveRecord_ExpectsAssocPropException ( $this->_class_name, $assoc->source_key );
			}
			
			$assoc->onSourceSave ( $this, $recursion - 1 );
		}
		
		// 引发after_create事件
		$this->_after_create ();
		$this->_event ( self::AFTER_CREATE );
		$this->_after_create_post ();
		
		// 清除所有属性的“脏”状态
		$this->_changed_props = array ();
	}
	
	/**
	 * 更新对象到数据库
	 *
	 * @param int $recursion
	 */
	protected function _update($recursion = 99) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		
		/**
		 * 仅在有属性更新时才引发 update 事件，并进行更新操作
		 */
		if (! empty ( $this->_changed_props )) {
			// 根据 update_autofill 设置对属性进行填充
			$this->_autofill ( 'update' );
			
			// 引发 before_update 事件
			$this->_before_update ();
			$this->_event ( self::BEFORE_UPDATE );
			$this->_before_update_post ();
			
			// 进行 update 验证
			$this->validate ( 'update', true );
			
			// 准备要更新到数据库的数据
			$save_data = array ();
			foreach ( $this->_props as $prop_name => $value ) {
				// 根据 update_reject 过滤掉不允许更新的属性
				if (isset ( $meta->update_reject [$prop_name] ) || ($meta->props [$prop_name] ['virtual'] && ! isset ( $meta->table_meta [$prop_name] ))) {
					continue;
				}
				// 只有指定为脏状态的属性才更新到数据库
				if (isset ( $this->_changed_props [$prop_name] )) {
					$save_data [$meta->props2fields [$prop_name]] = $value;
				}
			}
			
			if (! empty ( $save_data )) {
				// 确定更新条件
				$conditions = array ();
				foreach ( $meta->table->getPK () as $field_name ) {
					$prop_name = $meta->fields2props [$field_name];
					unset ( $save_data [$field_name] );
					$conditions [$field_name] = $this->_props [$prop_name];
				}
				if (! empty ( $save_data )) {
					// 将名值对保存到数据库
					$meta->table->update ( $save_data, $conditions );
				}
			}
		}
		
		// 遍历关联的对象，并调用对象的save()方法
		foreach ( $meta->associations as $prop => $assoc ) {
			if (! isset ( $this->_props [$prop] )) {
				continue;
			}
			
			$assoc->init ();
			$source_key_value = $this->{$assoc->source_key};
			
			if (strlen ( $source_key_value ) == 0) {
				throw new QDB_ActiveRecord_ExpectsAssocPropException ( $this->_class_name, $assoc->source_key );
			}
			
			$assoc->onSourceSave ( $this, $recursion - 1 );
		}
		
		// 引发 after_update 事件
		$this->_after_update ();
		$this->_event ( self::AFTER_UPDATE );
		$this->_after_update_post ();
		
		// 清除所有属性的“脏”状态
		$this->_changed_props = array ();
	}
	
	/**
	 * 替换数据库中的对象，如果不存在则创建新记录
	 *
	 * @param int $recursion
	 */
	protected function _replace($recursion = 99) {
		$_changes = $this->_changed_props;
		if (empty ( $_changes ))
			return;
		
		/**
		 * 数据库本身并不支持 replace 操作，所以只能是通过 insert 操作来模拟
		 */
		try {
			$this->_create ( $recursion );
		} catch ( QDB_Exception_DuplicateKey $ex ) {
			$this->_changed_props = $_changes;
			$this->_update ( $recursion );
		}
	}
	
	/**
	 * 对当前对象的属性进行自动填充
	 *
	 * @param string $mode
	 */
	protected function _autofill($mode) {
		$meta = self::$_meta [$this->_class_name];
		/* @var $meta QDB_ActiveRecord_Meta */
		$fill_props = ($mode == 'create') ? $meta->create_autofill : $meta->update_autofill;
		
		foreach ( $fill_props as $prop => $fill ) {
			if ($fill === self::AUTOFILL_DATETIME) {
				$this->_props [$prop] = date ( 'Y-m-d H:i:s', CURRENT_TIMESTAMP );
			} elseif ($fill === self::AUTOFILL_TIMESTAMP) {
				$this->_props [$prop] = intval ( CURRENT_TIMESTAMP );
			} elseif ($fill === self::AUTOFILL_DATE) {
				$this->_props [$prop] = date ( 'Y-m-d', CURRENT_TIMESTAMP );
			} elseif ($fill === self::AUTOFILL_TIME) {
				$this->_props [$prop] = date ( 'H:i:s', CURRENT_TIMESTAMP );
			} elseif (! is_array ( $fill )) {
				$this->_props [$prop] = $fill;
			} else {
				$this->_props [$prop] = self::_typed ( call_user_func ( $fill ), $meta->props [$prop] ['ptype'] );
			}
			// 设置“脏”状态
			$this->_changed_props [$prop] = true;
		}
	}
	
	/**
	 * 返回类型化以后的值
	 *
	 * @param mixed $value
	 * @param string $ptype
	 *
	 * @return mixed
	 */
	static protected function _typed($value, $ptype) {
		switch ($ptype) {
			case 'int1' :
			case 'int2' :
			case 'int3' :
			case 'int4' :
			case 'timestamp' :
				return intval ( $value );
			case 'float' :
			case 'double' :
			case 'dec' :
				return doubleval ( $value );
			case 'bool' :
				return ( bool ) $value;
			case 'date' :
			case 'datetime' :
				return empty ( $value ) ? null : $value;
		}
		
		return $value;
	}
	
	/**
	 * 事件回调：开始验证之前
	 */
	protected function _before_validate() {
	}
	
	protected function _before_validate_post() {
	}
	
	/**
	 * 事件回调：为创建记录进行的验证开始之前
	 */
	protected function _before_validate_on_create() {
	}
	
	protected function _before_validate_on_create_post() {
	}
	
	/**
	 * 事件回调：为创建记录进行的验证完成之后
	 */
	protected function _after_validate_on_create() {
	}
	
	protected function _after_validate_on_create_post() {
	}
	
	/**
	 * 事件回调：为更新记录进行的验证开始之前
	 */
	protected function _before_validate_on_update() {
	}
	
	protected function _before_validate_on_update_post() {
	}
	
	/**
	 * 事件回调：为更新记录进行的验证完成之后
	 */
	protected function _after_validate_on_update() {
	}
	
	protected function _after_validate_on_update_post() {
	}
	
	/**
	 * 事件回调：验证完成之后
	 */
	protected function _after_validate() {
	}
	
	protected function _after_validate_post() {
	}
	
	/**
	 * 事件回调：保存记录之前
	 */
	protected function _before_save() {
	}
	
	protected function _before_save_post() {
	}
	
	/**
	 * 事件回调：保存记录之后
	 */
	protected function _after_save() {
	}
	
	protected function _after_save_post() {
	}
	
	/**
	 * 事件回调：创建记录之前
	 */
	protected function _before_create() {
	}
	
	protected function _before_create_post() {
	}
	
	/**
	 * 事件回调：创建记录之后
	 */
	protected function _after_create() {
	}
	
	protected function _after_create_post() {
	}
	
	/**
	 * 事件回调：更新记录之前
	 */
	protected function _before_update() {
	}
	
	protected function _before_update_post() {
	}
	
	/**
	 * 事件回调：更新记录之后
	 */
	protected function _after_update() {
	}
	
	protected function _after_update_post() {
	}
	
	/**
	 * 事件回调：删除记录之前
	 */
	protected function _before_destroy() {
	}
	
	protected function _before_destroy_post() {
	}
	
	/**
	 * 事件回调：删除记录之后
	 */
	protected function _after_destroy() {
	}
	
	protected function _after_destroy_post() {
	}
	
	/**
	 * 事件回调：对象构造之后
	 */
	protected function _after_initialize() {
	}
	
	protected function _after_initialize_post() {
	}
}


